/**
 * Copyright 2014 opencxa.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.cxa.rpc.messages;

import java.util.HashMap;
import java.util.Map;

import org.cxa.rpc.provider.RpcException;

import com.google.gson.annotations.Expose;


/**
 * Note: cannot be abstract due to GSON parsing
 * @author Christopher Armenio
 */
public class RpcMessage
{
	public static final Integer MESSAGE_VERSION = 1;
	
	
	public enum MessageType
	{
		KEEPALIVE,
		KEEPALIVE_RESPONSE,
		REQUEST,
		RESPONSE,
		NOTIFICATION,
		UNKNOWN
	}
	
	
	private class Permissions
	{
		private Boolean group = false;
		private Boolean user = false;
		
		private Permissions(Boolean groupIn, Boolean userIn)
		{
			this.group = groupIn;
			this.user = userIn;
		}
	}
	
	
	@Expose private Integer msgVersion = null;
	@Expose private MessageType msgType = MessageType.UNKNOWN;
	
	@Expose protected Integer id = null;
	@Expose protected String dest = null;
	@Expose protected String src = null;
	@Expose protected String cmd = null;
	@Expose protected Permissions permissions = null;
	@Expose protected Map<String, Object> params = null;
	
	@Expose protected Boolean success = null;
	@Expose protected Object retValue = null;
	@Expose protected RpcException error = null;
	
	@Expose protected String subject = null;
	
	
	// this field is not serialized/deserialized
	protected Map<String, Object> metaData = new HashMap<String, Object>();
	
	
	/*
	 * required for gson parsing
	 */
	public RpcMessage()
	{
	}
	
	
	protected RpcMessage(RpcMessage msgIn)
	{
		this.msgVersion = msgIn.msgVersion;
		this.msgType = msgIn.msgType;
		this.id = msgIn.id;
		this.dest = msgIn.dest;
		this.src = msgIn.src;
		this.cmd = msgIn.cmd;
		this.params = msgIn.params;
		this.success = msgIn.success;
		this.subject = msgIn.subject;
		this.metaData = msgIn.metaData;
		this.retValue = msgIn.retValue;
		this.error = msgIn.error;
	}

	
	/*
	 * KeepAlive / KeepAliveResponse
	 */
	protected RpcMessage(MessageType msgTypeIn)
	{
		this.msgVersion = MESSAGE_VERSION;
		this.msgType = msgTypeIn;
	}
	
	
	/*
	 * Notification
	 */
	protected RpcMessage(String subjectIn, Map<String, Object> paramsIn)
	{	
		this.msgVersion = MESSAGE_VERSION;
		this.msgType = MessageType.NOTIFICATION;
		this.subject = subjectIn;
		this.params = paramsIn;
	}
	
	
	/*
	 * request
	 */
	protected RpcMessage(String destPathIn, String cmdIn, Map<String, Object> paramsIn)
	{
		this.msgVersion = MESSAGE_VERSION;
		this.msgType = MessageType.REQUEST;
		this.dest = destPathIn;
		this.cmd = cmdIn;
		this.params = paramsIn;
	}
	
	
	/*
	 * response
	 */
	protected RpcMessage(Integer idIn, String destPathIn, String cmdIn, Boolean successIn, Object retValIn, RpcException errIn)
	{
		this.msgVersion = MESSAGE_VERSION;
		this.msgType = MessageType.RESPONSE;
		this.dest = destPathIn;
		this.id = idIn;
		this.cmd = cmdIn;
		this.success = successIn;
		this.retValue = retValIn;
		this.error = errIn;
	}
	
	
	public MessageType getMsgType()
	{
		return msgType;
	}


	public Integer getMsgVersion()
	{
		return msgVersion;
	}
	
	
	public String getSource()
	{
		return this.src;
	}
	
	
	public void setSource(String sourceIn)
	{
		this.src = sourceIn;
	}
	
	
	public void setPermissions(Boolean groupIn, Boolean userIn)
	{
		if( this.permissions == null ) this.permissions = new Permissions(groupIn, userIn);
	}
	
	
	public Boolean getPermissions_group()
	{
		return (this.permissions == null) ? false : this.permissions.group;
	}
	
	
	public Boolean getPermissions_user()
	{
		return (this.permissions == null) ? false : this.permissions.user;
	}
	
	
	public Object getMetaData_byFieldName(String fieldNameIn)
	{
		return this.metaData.get(fieldNameIn);
	}
	
	
	public void setMetaData(String fieldNameIn, Object metaDataIn)
	{
		this.metaData.put(fieldNameIn, metaDataIn);
	}
	
	
	@Override
	public String toString()
	{
		return String.format("%s.%s", this.getClass().getSimpleName(), System.identityHashCode(this));
	}
	
	
	public static String[] parsePathComponents(String destPathIn)
	{		
		// every path must start with '/'...but we'll strip it out immediately
		if( !destPathIn.startsWith("/") )
		{
			return null;
		}
		destPathIn = destPathIn.substring(1);
		
		// now split the path components and examine
		return destPathIn.split("/");
	}
}
